﻿//Copyright (C) <2013>  <jonathan cleeve norton> All Rights Reserved 
//Contact jon.norton@fin-plus.co.uk website <http://www.fin-plus.co.uk/>
using System;
using System.Collections.Generic;
using System.Linq;
using FinPlusCommon;
using log = Logger.Logger;

namespace FinPlusComponents
{
    public class FinPlusEquate: FinPlusComponent
    {
        public override string[] TestStates { get { return new string[] { "TestString", "IsValid" }; } }
        public override IFunctions Adaptor { get { return _adaptor; } }
        public override string TestString { get { return Adaptor.ToString(); } }

        private enum Params { LevelUpdate };
        private readonly IDictionary<string, IFinPlusComp> _conns;
        private readonly IDictionary<string, IFunction> _index;
        private readonly IDictionary<string, string> _fields, _links, _expressions, _totals;
        private readonly IList<string> _keyLabels;
        private readonly IFunctions _adaptor;
        private string _name;
        private int _id; 

        //construct
        public FinPlusEquate(IDictionary<string, IFinPlusComp> conn, string name, string[] keyLables, IDictionary<string, string> fields,
            IDictionary<string, string> links, IDictionary<string, string> expressions, IDictionary<string, string> totals)
        {
            _conns = conn;
            _name = name;
            _keyLabels = keyLables;
            _fields = fields;
            _links = links;
            _expressions = expressions;
            _totals = totals;
            _index = new Dictionary<string, IFunction>();
            _adaptor = new Functions();

            Load();
        }

        //common control interface
        public override void Dispose() 
        {
            foreach (var conn in _conns.Values)
                conn.Dispose();
        }

        //private
        public void Load()
        {
            foreach (var kvp in _conns)
                AddFields(kvp.Value.Adaptor, kvp.Key);

            foreach (var kvp in _conns)
                AddLinks(kvp.Value.Adaptor, kvp.Key);

            AddExpressions(_adaptor);
            AddTotals(_adaptor);

            IsValid = true;
        }

        private void AddFields(IFunctions funcs, string name)
        {
            try
            {
                if (_fields == null) return;

                foreach (var f in _fields.Where(s => s.Value.Equals(name)))
                    foreach (var func in funcs.Values)
                        AddField(func, f.Key);
            }
            catch (Exception e)
            {
                IsValid = log.Log(Logger.Level.Error, Id, Config, e.Message, e);
            }
        }

        private void AddField(IFunction func, string name)
        {
            try
            {
                var val = func.Get(name);
                if (val == null) 
                    return;

                var f = GetFunction(func, name);
                val.Index = -1;
                f.TryAdd(val); 
            }
            catch (Exception e)
            {
                IsValid = log.Log(Logger.Level.Error, Id, Config, e.Message, e);
            }
        }

        private void AddLinks(IFunctions funcs, string name)
        {
            try
            {
                if (_links == null) return;

                foreach (var f in _links.Where(s => s.Key.Split('.')[0].Equals(name)))
                    foreach (var func in funcs.Values)
                        AddLink(func, f.Value, f.Key);
            }
            catch (Exception e)
            {
                IsValid = log.Log(Logger.Level.Error, Id, Config, e.Message, e);
            }
        }

        private void AddLink(IFunction func, string left, string right)
        {
            try
            {
                string[] l = left.Split('.'), r = right.Split('.');
                var label = func[l[1]].ToString();
                foreach (var model in _adaptor.ToList(l[0], label))
                    model.TryAdd(func.Get<double>(r[1], 0.0));
            }
            catch (Exception e)
            {
                IsValid = log.Log(Logger.Level.Error, Id, Config, e.Message, e);
            }
        }

        private void AddExpressions(IFunctions funcs)
        {
            try
            {
                if (_expressions == null) return;

                foreach (var f in _expressions)
                    foreach (var func in funcs.Values)
                        AddExpression(func, f.Key, f.Value);
            }
            catch (Exception e)
            {
                IsValid = log.Log(Logger.Level.Error, Id, Config, e.Message, e);
            }
        }

        private void AddExpression(IFunction func, string name, string expression)
        {
            try
            {
                var item = GetFunction(func, name).Add(name, 0.0);
                item.Evaluate(func, expression);
            }
            catch (Exception e)
            {
                IsValid = log.Log(Logger.Level.Error, Id, Config, e.Message, e);
            }
        }

        private void AddTotals(IFunctions funcs)
        {
            try
            {
                if (_totals == null) return;
                
                foreach (var total in _totals)
                    AddTotal(funcs, total.Value, total.Key);
            }
            catch (Exception e)
            {
                IsValid = log.Log(Logger.Level.Error, Id, Config, e.Message, e);
            }
        }

        private void AddTotal(IFunctions funcs, string left, string right)
        {
            try
            {
                string[] l = left.Split('.'), r = right.Split('.');
                foreach (var func in funcs.Values.Where(f => f[l[0]].Equals(l[1])))
                {
                    var v = func.ToList(r[1], false);
                    if (v.Count() < 1) continue;
                    var item = v.First();
                    var link = func.Where(i => i.Name.Equals(r[0])).First().Value.ToString();
                    var list = new List<IItem>();
                    foreach (var func2 in funcs.Values)
                        if (func2[r[0]].Equals(link) && !func2.Equals(func))
                            list.AddRange(func2.ToList(r[1], false));
                    item.Total(list);
                }
            }
            catch (Exception e)
            {
                IsValid = log.Log(Logger.Level.Error, Id, Config, e.Message, e);
            }
        }

        private IFunction GetFunction(IFunction func, string name)
        {
            var key = string.Empty;
            foreach (var keyLabel in _keyLabels)
                key += func[keyLabel] + "|";

            key = key.TrimEnd('|');
                    
            if (_index.ContainsKey(key)) 
                return _index[key];

            return AddFunction(func, key);
        }

        private IFunction AddFunction(IFunction func, string key)
        {
            _id++;
            IFunction f;
            _index[key] = f = new Function(_name, _id.ToString(), User, _id);
            foreach (var i in func)
                if (_keyLabels.Contains(i.Name))
                    f.TryAdd(i);

            _adaptor.Add(f);

            return f;
        }
    }
}
